package com.zhihao.trigger.http;

import com.alibaba.fastjson.JSON;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.zhihao.domain.activity.model.entity.*;
import com.zhihao.domain.activity.model.valobj.OrderTradeTypeVO;
import com.zhihao.domain.activity.service.IRaffleActivityAccountQuotaService;
import com.zhihao.domain.activity.service.IRaffleActivityPartakeService;
import com.zhihao.domain.activity.service.IRaffleActivitySkuProductService;
import com.zhihao.domain.activity.service.armory.IActivityArmory;
import com.zhihao.domain.award.model.entity.UserAwardRecordEntity;
import com.zhihao.domain.award.model.valobj.AwardStateVO;
import com.zhihao.domain.award.service.IAwardService;
import com.zhihao.domain.credit.model.entity.CreditAccountEntity;
import com.zhihao.domain.credit.model.entity.TradeEntity;
import com.zhihao.domain.credit.model.valobj.TradeNameVO;
import com.zhihao.domain.credit.model.valobj.TradeTypeVO;
import com.zhihao.domain.credit.service.ICreditAdjustService;
import com.zhihao.domain.rebate.model.entity.BehaviorEntity;
import com.zhihao.domain.rebate.model.entity.BehaviorRebateOrderEntity;
import com.zhihao.domain.rebate.model.valobj.BehaviorTypeVO;
import com.zhihao.domain.rebate.service.IBehaviorRebateService;
import com.zhihao.domain.strategy.model.entity.RaffleAwardEntity;
import com.zhihao.domain.strategy.model.entity.RaffleFactorEntity;
import com.zhihao.domain.strategy.service.IRaffleStrategy;
import com.zhihao.domain.strategy.service.armory.IStrategyArmory;
import com.zhihao.trigger.api.IRaffleActivityService;
import com.zhihao.trigger.api.dto.*;
import com.zhihao.types.annotations.DCCValue;
import com.zhihao.types.annotations.RateLimiterAccessInterceptor;
import com.zhihao.types.enums.ResponseCode;
import com.zhihao.types.exception.AppException;
import com.zhihao.types.model.Response;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author Jackson
 * @description 抽奖活动服务 注意；在不引用 application/case 层的时候，就需要让接口实现层来做领域的串联。一些较大规模的系统，需要加入 case 层。
 * @create 2024-04-13 09:42
 */
@Slf4j
@RestController
@CrossOrigin("${app.config.cross-origin}")
@RequestMapping("/api/${app.config.api-version}/raffle/activity/")
@DubboService(version = "1.0")
public class RaffleActivityController implements IRaffleActivityService {

    @Resource
    private IRaffleActivityPartakeService raffleActivityPartakeService;
    @Resource
    private IRaffleActivityAccountQuotaService raffleActivityAccountQuotaService;
    @Resource
    private IRaffleStrategy raffleStrategy;
    @Resource
    private IRaffleActivitySkuProductService raffleActivitySkuProductService;

    @Resource
    private IAwardService awardService;
    @Resource
    private IActivityArmory activityArmory;
    @Resource
    private IStrategyArmory strategyArmory;
    @Resource
    private IBehaviorRebateService behaviorRebateService;
    @Resource
    private ICreditAdjustService creditAdjustService;

    private final SimpleDateFormat dateFormatDay = new SimpleDateFormat("yyyyMMdd");

    /* 降级开关，默认不降级（即：接口正常放行） */
    @DCCValue("degradeSwitch:close")
    private String degradeSwitch;

    /**
     * 活动装配 - 数据预热 | 把活动配置的对应的 sku 一起装配
     *
     * @param activityId 活动ID
     * @return 装配结果
     * <p>
     * 接口：<a href="http://localhost:8091/api/v1/raffle/activity/armory">/api/v1/raffle/activity/armory</a>
     * 入参：{"activityId":100001,"userId":"xiaofuge"}
     *
     * curl --request GET \
     *   --url 'http://localhost:8091/api/v1/raffle/activity/armory?activityId=100301'
     *
     *   25-04-09.22:36:43.355 [http-nio-8091-exec-1] INFO  RaffleActivityController - 活动装配，数据预热，开始 activityId:100301
     * 25-04-09.22:36:45.115 [http-nio-8091-exec-1] INFO  O1Algorithm            - 抽奖算法 O(1) 装配 key:100006
     * 25-04-09.22:36:45.423 [http-nio-8091-exec-1] INFO  O1Algorithm            - 抽奖算法 O(1) 装配 key:100006_70:106,107
     * 25-04-09.22:36:45.564 [http-nio-8091-exec-1] INFO  O1Algorithm            - 抽奖算法 O(1) 装配 key:100006_10:102,103
     * 25-04-09.22:36:45.704 [http-nio-8091-exec-1] INFO  O1Algorithm            - 抽奖算法 O(1) 装配 key:100006_1000:104,105
     * 25-04-09.22:36:45.844 [http-nio-8091-exec-1] INFO  RaffleActivityController - 活动装配，数据预热，完成 activityId:100301
     *
     */
    @RequestMapping(value = "armory", method = RequestMethod.GET)
    @Override
    public Response<Boolean> armory(@RequestParam Long activityId) {
        try {
            log.info("活动装配，数据预热，开始 activityId:{}", activityId);
            // 1. 活动装配
            activityArmory.assembleActivitySkuByActivityId(activityId);
            // 2. 策略装配
            strategyArmory.assembleLotteryStrategyByActivityId(activityId);
            Response<Boolean> response = Response.<Boolean>builder()
                    .code(ResponseCode.SUCCESS.getCode())
                    .info(ResponseCode.SUCCESS.getInfo())
                    .data(true)
                    .build();
            log.info("活动装配，数据预热，完成 activityId:{}", activityId);
            return response;
        } catch (Exception e) {
            log.error("活动装配，数据预热，失败 activityId:{}", activityId, e);
            return Response.<Boolean>builder()
                    .code(ResponseCode.UN_ERROR.getCode())
                    .info(ResponseCode.UN_ERROR.getInfo())
                    .build();
        }
    }

    /**
     * 判断是否签到接口(查询用户当前是否已签到---button显示状态)
     * <p>
     * curl -X POST http://localhost:8091/api/v1/raffle/activity/is_calendar_sign_rebate -d "userId=xiaofuge" -H "Content-Type: application/x-www-form-urlencoded"
     *
     * 25-04-10.13:36:13.345 [http-nio-8091-exec-1] INFO  RaffleActivityController - 查询用户是否完成日历签到返利开始 userId:manman
     * 25-04-10.13:36:13.436 [http-nio-8091-exec-1] INFO  RaffleActivityController - 查询用户是否完成日历签到返利完成 userId:manman 返利流水数量-orders.size:2
     *
     */
    @RequestMapping(value = "is_calendar_sign_rebate", method = RequestMethod.POST)
    @Override
    public Response<Boolean> isCalendarSignRebate(@RequestParam String userId) {
        try {
            log.info("查询用户是否完成日历签到返利开始 userId:{}", userId);
            // outBusinessNo=day ： 也就显式地指定了是查询用户是否完成当日签到
            String outBusinessNo = dateFormatDay.format(new Date());
            List<BehaviorRebateOrderEntity> behaviorRebateOrderEntities = behaviorRebateService.queryOrderByOutBusinessNo(userId, outBusinessNo);
            log.info("查询用户是否完成日历签到返利完成 userId:{} 返利流水数量-orders.size:{}", userId, behaviorRebateOrderEntities.size());
            return Response.<Boolean>builder()
                    .code(ResponseCode.SUCCESS.getCode())
                    .info(ResponseCode.SUCCESS.getInfo())
                    .data(!behaviorRebateOrderEntities.isEmpty()) // 只要不为空，则表示已经做了签到
                    .build();
        } catch (Exception e) {
            log.error("查询用户是否完成日历签到返利失败 userId:{}", userId, e);
            return Response.<Boolean>builder()
                    .code(ResponseCode.UN_ERROR.getCode())
                    .info(ResponseCode.UN_ERROR.getInfo())
                    .data(false)
                    .build();
        }
    }

    /**
     * 日历签到返利接口(签到的奖励可以是多个：积分奖励+抽奖次数奖励...)
     *      用户返利的库表操作：用户行为返利记录流水+奖品发放mq消息task补偿表 + mq消息发送+（job定时任务扫task表） + listener监听返利执行
     * @param userId 用户ID
     * @return 签到返利结果
     * <p>
     * 接口：<a href="http://localhost:8091/api/v1/raffle/activity/calendar_sign_rebate">/api/v1/raffle/activity/calendar_sign_rebate</a>
     * 入参：xiaofuge
     * <p>
     * curl -X POST http://localhost:8091/api/v1/raffle/activity/calendar_sign_rebate -d "userId=xiaofuge" -H "Content-Type: application/x-www-form-urlencoded"
     */
    @RequestMapping(value = "calendar_sign_rebate", method = RequestMethod.POST)
    @Override
    public Response<Boolean> calendarSignRebate(@RequestParam String userId) {
        try {
            log.info("日历签到返利开始 userId:{}", userId);
            BehaviorEntity behaviorEntity = new BehaviorEntity();
            behaviorEntity.setUserId(userId);
            behaviorEntity.setBehaviorTypeVO(BehaviorTypeVO.SIGN);
            // 签到业务的防重复id_时间----防止用户一天内签到多次
            behaviorEntity.setOutBusinessNo(dateFormatDay.format(new Date()));

            // (签到的奖励可以是多个：积分奖励+抽奖次数奖励...) 所以返回的是orderIds-返利订单记录s
            List<String> orderIds = behaviorRebateService.createOrder(behaviorEntity);
            log.info("日历签到返利完成 userId:{} orderIds: {}", userId, JSON.toJSONString(orderIds));
            return Response.<Boolean>builder()
                    .code(ResponseCode.SUCCESS.getCode())
                    .info(ResponseCode.SUCCESS.getInfo())
                    .data(true)
                    .build();
        } catch (AppException e) {
            log.error("日历签到返利异常 userId:{} ", userId, e);
            return Response.<Boolean>builder()
                    .code(e.getCode())
                    .info(e.getInfo())
                    .build();
        } catch (Exception e) {
            log.error("日历签到返利失败 userId:{}", userId);
            return Response.<Boolean>builder()
                    .code(ResponseCode.UN_ERROR.getCode())
                    .info(ResponseCode.UN_ERROR.getInfo())
                    .data(false)
                    .build();
        }
    }

    /**
     * 抽奖接口
     * （先“签到sign“获得免费的活动抽奖额度+用户积分，然后可以用于抽奖/兑换抽奖机会）
     *
     * @param request 请求对象
     * @return 抽奖结果
     * <p>
     * 接口：<a href="http://localhost:8091/api/v1/raffle/activity/draw">/api/v1/raffle/activity/draw</a>
     * 入参：{"activityId":100001,"userId":"xiaofuge"}
     *
     * curl --request POST \
     *   --url http://localhost:8091/api/v1/raffle/activity/draw \
     *   --header 'content-type: application/json' \
     *   --data '{
     *     "userId":"xiaofuge",
     *     "activityId": 100301
     * }'
     *
     * 25-04-10.14:42:42.186 [http-nio-8091-exec-7] INFO  RaffleActivityController - 活动抽奖，创建订单 userId:manman activityId:100301 orderId:389748358102
     * 25-04-10.14:42:42.188 [http-nio-8091-exec-7] INFO  DefaultRaffleStrategy  - 抽奖策略-责任链 userId:manman strategyId:100006
     * 25-04-10.14:42:42.241 [http-nio-8091-exec-7] INFO  BlacklistLogicChain    - 抽奖责任链-黑名单开始 userId:manman strategyId:100006 ruleModel:rule_blacklist
     * 25-04-10.14:42:42.333 [http-nio-8091-exec-7] INFO  BlacklistLogicChain    - 抽奖责任链-黑名单放行 userId: manman strategyId: 100006 ruleModel: rule_blacklist
     * 25-04-10.14:42:42.333 [http-nio-8091-exec-7] INFO  RuleWeightLogicChain   - 抽奖责任链-权重开始 userId: manman strategyId: 100006 ruleModel: rule_weight
     * 25-04-10.14:42:42.450 [http-nio-8091-exec-7] INFO  RuleWeightLogicChain   - 抽奖责任链-权重放行 userId: manman strategyId: 100006 ruleModel: rule_weight
     * 25-04-10.14:42:42.532 [http-nio-8091-exec-7] INFO  O1Algorithm            - 抽奖算法 O(1) 抽奖计算 key:100006
     * 25-04-10.14:42:42.632 [http-nio-8091-exec-7] INFO  DefaultLogicChain      - 抽奖责任链-默认处理 userId: manman strategyId: 100006 ruleModel: rule_default awardId: 101
     * 25-04-10.14:42:42.634 [http-nio-8091-exec-7] INFO  AbstractRaffleStrategy - 抽奖策略计算-责任链-结果 manman 100006 101 rule_default
     * 25-04-10.14:42:42.674 [http-nio-8091-exec-7] INFO  DefaultRaffleStrategy  - 抽奖策略-规则树 userId:manman strategyId:100006 awardId:101
     * 25-04-10.14:42:42.896 [http-nio-8091-exec-7] INFO  RuleStockLogicTreeNode - 规则过滤-库存扣减 userId:manman strategyId:100006 awardId:101
     * 25-04-10.14:42:42.962 [http-nio-8091-exec-7] INFO  RuleStockLogicTreeNode - 规则过滤-库存扣减-成功 userId:manman strategyId:100006 awardId:101
     * 25-04-10.14:42:43.038 [http-nio-8091-exec-7] INFO  DecisionTreeEngine     - 决策树引擎【规则树-兜底奖励】treeId:tree_luck_award node:rule_stock code:0001
     * 25-04-10.14:42:43.039 [http-nio-8091-exec-7] INFO  AbstractRaffleStrategy - 抽奖策略计算-规则树 manman 100006 101 null
     * 25-04-10.14:42:43.459 [http-nio-8091-exec-7] INFO  EventPublisher         - 发送MQ消息 topic:send_award message:{"data":{"awardId":101,"awardTitle":"随机积分","orderId":"389748358102","userId":"manman"},"id":"82981714130","timestamp":1744267363081}
     * 25-04-10.14:42:43.495 [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#3-1] INFO  SendAwardCustomer      - 监听用户奖品发送消息，发奖开始 topic: send_award message: {"data":{"awardId":101,"awardTitle":"随机积分","orderId":"389748358102","userId":"manman"},"id":"82981714130","timestamp":1744267363081}
     * 25-04-10.14:42:43.537 [http-nio-8091-exec-7] INFO  AwardService           - 中奖记录保存完成 userId:manman orderId:389748358102
     * 25-04-10.14:42:43.989 [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#3-1] INFO  SendAwardCustomer      - 监听用户奖品发送消息，发奖完成 topic: send_award message: {"data":{"awardId":101,"awardTitle":"随机积分","orderId":"389748358102","userId":"manman"},"id":"82981714130","timestamp":1744267363081}
     * 25-04-10.14:42:50.223 [scheduling-1    ] INFO  UpdateAwardStockJob    - 定时任务，更新奖品消耗库存成功 strategyId:100006 awardId:101
     * 限流配置
     * RateLimiterAccessInterceptor
     * key: 以用户ID作为拦截，这个用户访问次数限制
     * fallbackMethod：失败后的回调方法，方法出入参保持一样
     * permitsPerSecond：每秒的访问频次限制
     * blacklistCount：超过多少次都被限制了，还访问的，扔到黑名单里24小时
     */
    @RateLimiterAccessInterceptor(key = "userId", fallbackMethod = "drawRateLimiterError", permitsPerSecond = 1L, blacklistCount = 3)
    @HystrixCommand(commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "150")
    }, fallbackMethod = "drawHystrixError"
    )
    @RequestMapping(value = "draw", method = RequestMethod.POST)
    @Override
    public Response<ActivityDrawResponseDTO> draw(@RequestBody ActivityDrawRequestDTO request) {
        try {
            log.info("活动抽奖开始 userId:{} activityId:{}", request.getUserId(), request.getActivityId());
            // 0. 降级开关 【open:开启, close:关闭】
            if (StringUtils.isNotBlank(degradeSwitch) && "open".equals(degradeSwitch)) {
                return Response.<ActivityDrawResponseDTO>builder()
                        .code(ResponseCode.DEGRADE_SWITCH.getCode())
                        .info(ResponseCode.DEGRADE_SWITCH.getInfo())
                        .build();
            }

            // 1. 参数校验
            if (StringUtils.isBlank(request.getUserId()) || null == request.getActivityId()) {
                throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo());
            }

            // 2. 参与活动 - 创建参与记录订单
            UserRaffleOrderEntity orderEntity = raffleActivityPartakeService.createOrder(request.getUserId(), request.getActivityId());
            log.info("活动抽奖，创建订单 userId:{} activityId:{} orderId:{}", request.getUserId(), request.getActivityId(), orderEntity.getOrderId());

            // 3. 抽奖策略 - 执行抽奖
            RaffleAwardEntity raffleAwardEntity = raffleStrategy.performRaffle(RaffleFactorEntity.builder()
                    .userId(orderEntity.getUserId())
                    .strategyId(orderEntity.getStrategyId())
                    .endDateTime(orderEntity.getEndDateTime())
                    .build());

            // 4. 存放结果 - 写入中奖记录
            UserAwardRecordEntity userAwardRecord = UserAwardRecordEntity.builder()
                    .userId(orderEntity.getUserId())
                    .activityId(orderEntity.getActivityId())
                    .strategyId(orderEntity.getStrategyId())
                    .orderId(orderEntity.getOrderId())
                    .awardId(raffleAwardEntity.getAwardId())
                    .awardTitle(raffleAwardEntity.getAwardTitle())
                    .awardTime(new Date())
                    .awardState(AwardStateVO.create)
                    .awardConfig(raffleAwardEntity.getAwardConfig())
                    .build();

            awardService.saveUserAwardRecord(userAwardRecord);

            // 5. 返回结果
            return Response.<ActivityDrawResponseDTO>builder()
                    .code(ResponseCode.SUCCESS.getCode())
                    .info(ResponseCode.SUCCESS.getInfo())
                    .data(ActivityDrawResponseDTO.builder()
                            .awardId(raffleAwardEntity.getAwardId())
                            .awardTitle(raffleAwardEntity.getAwardTitle())
                            .awardIndex(raffleAwardEntity.getSort())
                            .build())
                    .build();
        } catch (AppException e) {
            log.error("活动抽奖失败 userId:{} activityId:{}", request.getUserId(), request.getActivityId(), e);
            return Response.<ActivityDrawResponseDTO>builder()
                    .code(e.getCode())
                    .info(e.getInfo())
                    .build();
        } catch (Exception e) {
            log.error("活动抽奖失败 userId:{} activityId:{}", request.getUserId(), request.getActivityId(), e);
            return Response.<ActivityDrawResponseDTO>builder()
                    .code(ResponseCode.UN_ERROR.getCode())
                    .info(ResponseCode.UN_ERROR.getInfo())
                    .build();
        }
    }

    // 接口限流 返回方法
    public Response<ActivityDrawResponseDTO> drawRateLimiterError(@RequestBody ActivityDrawRequestDTO request) {
        log.info("活动抽奖限流 userId:{} activityId:{}", request.getUserId(), request.getActivityId());
        return Response.<ActivityDrawResponseDTO>builder()
                .code(ResponseCode.RATE_LIMITER.getCode())
                .info(ResponseCode.RATE_LIMITER.getInfo())
                .build();
    }

    // 接口熔断 返回方法
    public Response<ActivityDrawResponseDTO> drawHystrixError(@RequestBody ActivityDrawRequestDTO request) {
        log.info("活动抽奖熔断 userId:{} activityId:{}", request.getUserId(), request.getActivityId());
        return Response.<ActivityDrawResponseDTO>builder()
                .code(ResponseCode.HYSTRIX.getCode())
                .info(ResponseCode.HYSTRIX.getInfo())
                .build();
    }


    /**
     * 查询账户额度
     * <p>
     * curl --request POST \
     * --url http://localhost:8091/api/v1/raffle/activity/query_user_activity_account \
     * --header 'content-type: application/json' \
     * --data '{
     * "userId":"xiaofuge",
     * "activityId": 100301
     * }'
     * 25-04-10.13:58:31.110 [http-nio-8091-exec-15] INFO  RaffleActivityController - 查询用户活动账户开始 userId:manman activityId:100301
     * 25-04-10.13:58:31.407 [http-nio-8091-exec-15] INFO  RaffleActivityController - 查询用户活动账户完成 userId:manman activityId:100301
     * dto:{"activityId":100301,"dayCount":1,"dayCountSurplus":0,"monthCount":1,"monthCountSurplus":0,"totalCount":1,"totalCountSurplus":0,"userId":"manman"}
     *
     */
    @RequestMapping(value = "query_user_activity_account", method = RequestMethod.POST)
    @Override
    public Response<UserActivityAccountResponseDTO> queryUserActivityAccount(@RequestBody UserActivityAccountRequestDTO request) {
        try {
            log.info("查询用户活动账户开始 userId:{} activityId:{}", request.getUserId(), request.getActivityId());
            // 1. 参数校验
            if (StringUtils.isBlank(request.getUserId()) || null == request.getActivityId()) {
                throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo());
            }
            ActivityAccountEntity activityAccountEntity = raffleActivityAccountQuotaService.queryActivityAccountEntity(request.getActivityId(), request.getUserId());
            UserActivityAccountResponseDTO userActivityAccountResponseDTO = UserActivityAccountResponseDTO.builder()
                    .userId(request.getUserId())
                    .activityId(request.getActivityId())
                    .totalCount(activityAccountEntity.getTotalCount())
                    .totalCountSurplus(activityAccountEntity.getTotalCountSurplus())
                    .dayCount(activityAccountEntity.getDayCount())
                    .dayCountSurplus(activityAccountEntity.getDayCountSurplus())
                    .monthCount(activityAccountEntity.getMonthCount())
                    .monthCountSurplus(activityAccountEntity.getMonthCountSurplus())
                    .build();
            log.info("查询用户活动账户完成 userId:{} activityId:{} dto:{}", request.getUserId(), request.getActivityId(), JSON.toJSONString(userActivityAccountResponseDTO));
            return Response.<UserActivityAccountResponseDTO>builder()
                    .code(ResponseCode.SUCCESS.getCode())
                    .info(ResponseCode.SUCCESS.getInfo())
                    .data(userActivityAccountResponseDTO)
                    .build();
        } catch (Exception e) {
            log.error("查询用户活动账户失败 userId:{} activityId:{}", request.getUserId(), request.getActivityId(), e);
            return Response.<UserActivityAccountResponseDTO>builder()
                    .code(ResponseCode.UN_ERROR.getCode())
                    .info(ResponseCode.UN_ERROR.getInfo())
                    .build();
        }
    }

    /**
     * 查询活动绑定的sku商品列表：签到sku、支付sku等
     * @author Jackson
     * @date 2025-04-10 13:59
     * @return Response<List<SkuProductResponseDTO>>
     *
     * {
     * 	"code": "0000",
     * 	"info": "调用成功",
     * 	"data": [
     * 		        {
     * 			"sku": 9011,
     * 			"activityId": 100301,
     * 			"activityCountId": 11101,
     * 			"stockCount": 100,
     * 			"stockCountSurplus": 100,
     * 			"productAmount": 200,
     * 			"activityCount": {
     * 				"totalCount": 150,
     * 				"dayCount": 150,
     * 				"monthCount": 150
     *            }
     *        },
     *        {
     * 			"sku": 9012,
     * 			"activityId": 100301,
     * 			"activityCountId": 11102,
     * 			"stockCount": 100000,
     * 			"stockCountSurplus": 99885,
     * 			"productAmount": 100,
     * 			"activityCount": {
     * 				"totalCount": 50,
     * 				"dayCount": 50,
     * 				"monthCount": 50
     *            }
     *        },
     *        {
     * 			"sku": 9013,
     * 			"activityId": 100301,
     * 			"activityCountId": 11103,
     * 			"stockCount": 100000,
     * 			"stockCountSurplus": 99884,
     * 			"productAmount": 20,
     * 			"activityCount": {
     * 				"totalCount": 5,
     * 				"dayCount": 5,
     * 				"monthCount": 5
     *            }
     *        },
     *        {
     * 			"sku": 9014,
     * 			"activityId": 100301,
     * 			"activityCountId": 11104,
     * 			"stockCount": 100000,
     * 			"stockCountSurplus": 99863,
     * 			"productAmount": 5,
     * 			"activityCount": {
     * 				"totalCount": 1,
     * 				"dayCount": 1,
     * 				"monthCount": 1
     *            }
     *        }
     * 	]
     * }
     **/
    @RequestMapping(value = "query_sku_product_list_by_activity_id", method = RequestMethod.POST)
    @Override
    public Response<List<SkuProductResponseDTO>> querySkuProductListByActivityId(Long activityId) {
        try {
            log.info("查询sku商品集合开始 activityId:{}", activityId);
            // 1. 参数校验
            if (null == activityId) {
                throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo());
            }
            // 2. 查询商品&封装数据
            List<SkuProductEntity> skuProductEntities = raffleActivitySkuProductService.querySkuProductEntityListByActivityId(activityId);
            List<SkuProductResponseDTO> skuProductResponseDTOS = new ArrayList<>(skuProductEntities.size());
            for (SkuProductEntity skuProductEntity : skuProductEntities) {

                SkuProductResponseDTO.ActivityCount activityCount = new SkuProductResponseDTO.ActivityCount();
                activityCount.setTotalCount(skuProductEntity.getActivityCount().getTotalCount());
                activityCount.setMonthCount(skuProductEntity.getActivityCount().getMonthCount());
                activityCount.setDayCount(skuProductEntity.getActivityCount().getDayCount());

                SkuProductResponseDTO skuProductResponseDTO = new SkuProductResponseDTO();
                skuProductResponseDTO.setSku(skuProductEntity.getSku());
                skuProductResponseDTO.setActivityId(skuProductEntity.getActivityId());
                skuProductResponseDTO.setActivityCountId(skuProductEntity.getActivityCountId());
                skuProductResponseDTO.setStockCount(skuProductEntity.getStockCount());
                skuProductResponseDTO.setStockCountSurplus(skuProductEntity.getStockCountSurplus());
                skuProductResponseDTO.setProductAmount(skuProductEntity.getProductAmount());
                skuProductResponseDTO.setActivityCount(activityCount);
                skuProductResponseDTOS.add(skuProductResponseDTO);
            }

            log.info("查询sku商品集合完成 activityId:{} skuProductResponseDTOS:{}", activityId, JSON.toJSONString(skuProductResponseDTOS));
            return Response.<List<SkuProductResponseDTO>>builder()
                    .code(ResponseCode.SUCCESS.getCode())
                    .info(ResponseCode.SUCCESS.getInfo())
                    .data(skuProductResponseDTOS)
                    .build();
        } catch (Exception e) {
            log.error("查询sku商品集合失败 activityId:{}", activityId, e);
            return Response.<List<SkuProductResponseDTO>>builder()
                    .code(ResponseCode.UN_ERROR.getCode())
                    .info(ResponseCode.UN_ERROR.getInfo())
                    .build();
        }
    }

    /**
     * 查询用户积分账户amount
     * @author Jackson
     * @date 2025-04-10 14:04
     * @return Response<BigDecimal>
     *
     * {
     * 	"code": "0000",
     * 	"info": "调用成功",
     * 	"data": 107.1
     * }
     *
     **/
    @RequestMapping(value = "query_user_credit_account", method = RequestMethod.POST)
    @Override
    public Response<BigDecimal> queryUserCreditAccount(String userId) {
        try {
            log.info("查询用户积分值开始 userId:{}", userId);
            CreditAccountEntity creditAccountEntity = creditAdjustService.queryUserCreditAccount(userId);
            log.info("查询用户积分值完成 userId:{} adjustAmount:{}", userId, creditAccountEntity.getAdjustAmount());
            return Response.<BigDecimal>builder()
                    .code(ResponseCode.SUCCESS.getCode())
                    .info(ResponseCode.SUCCESS.getInfo())
                    .data(creditAccountEntity.getAdjustAmount())
                    .build();
        } catch (Exception e) {
            log.error("查询用户积分值失败 userId:{}", userId, e);
            return Response.<BigDecimal>builder()
                    .code(ResponseCode.UN_ERROR.getCode())
                    .info(ResponseCode.UN_ERROR.getInfo())
                    .build();
        }
    }


    /**
     * 积分兑换活动sku抽奖次数
     * @param request 请求对象「用户ID、商品ID」
     * @return 成功失败
     *
     *25-04-10.14:39:22.024 [http-nio-8091-exec-5] INFO  RaffleActivityController - 积分兑换商品开始 userId:manman sku:9014
     * 25-04-10.14:39:22.280 [http-nio-8091-exec-5] INFO  ActivityBaseActionChainNode - 活动责任链-基础信息【有效期、状态、库存(sku)】校验开始。sku:9014 activityId:100301
     * 25-04-10.14:39:22.280 [http-nio-8091-exec-5] INFO  ActivityBaseActionChainNode - 活动责任链-基础信息【有效期、状态、库存(sku)】校验成功。sku:9014 activityId:100301
     * 25-04-10.14:39:22.280 [http-nio-8091-exec-5] INFO  ActivitySkuStockActionChainNode - 活动责任链-商品库存处理【有效期、状态、库存(sku)】开始。sku:9014 activityId:100301
     * 25-04-10.14:39:22.351 [http-nio-8091-exec-5] INFO  ActivitySkuStockActionChainNode - 活动责任链-商品库存处理【有效期、状态、库存(sku)】成功。sku:9014 activityId:100301
     * 25-04-10.14:39:22.581 [http-nio-8091-exec-5] INFO  RaffleActivityController - 积分兑换商品，创建订单完成 userId:manman sku:9014 outBusinessNo:930571506479
     * 25-04-10.14:39:22.581 [http-nio-8091-exec-5] INFO  CreditAdjustService    - 账户积分额度开始 userId:manman tradeName:CONVERT_SKU amount:-5.00
     * 25-04-10.14:39:22.581 [http-nio-8091-exec-5] INFO  CreditAdjustService    - reverse积分反向操作，credit_exchange_raffle_count_success消息队列mq+task已构建！
     * 25-04-10.14:39:23.037 [http-nio-8091-exec-5] INFO  EventPublisher         - 发送MQ消息 topic:credit_exchange_raffle_count_success message:{"data":{"amount":-5.00,"orderId":"806400246293","outBusinessNo":"930571506479","userId":"manman"},"id":"86092399239","timestamp":1744267162581}
     * 25-04-10.14:39:23.075 [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#1-1] INFO  CreditExchangeRaffleCountSuccessCustomer - 监听积分账户调整成功消息，进行交易商品发货 topic: credit_exchange_raffle_count_success message: {"data":{"amount":-5.00,"orderId":"806400246293","outBusinessNo":"930571506479","userId":"manman"},"id":"86092399239","timestamp":1744267162581}
     * 25-04-10.14:39:23.121 [http-nio-8091-exec-5] INFO  CreditRepository       - 调整账户积分记录，发送MQ消息完成 userId: manman orderId:806400246293 topic: credit_exchange_raffle_count_success
     * 25-04-10.14:39:23.121 [http-nio-8091-exec-5] INFO  CreditAdjustService    - 账户积分额度完成 userId:manman orderId:806400246293
     * 25-04-10.14:39:23.121 [http-nio-8091-exec-5] INFO  RaffleActivityController - 积分兑换商品，支付订单完成  userId:manman sku:9014 orderId:806400246293
     */
    @RequestMapping(value = "credit_pay_exchange_sku", method = RequestMethod.POST)
    @Override
    public Response<Boolean> creditPayExchangeSku(@RequestBody SkuProductShopCartRequestDTO request) {
        try {
            /*
                todo:”数据的最终一致性“
                 task表仅能保证mq消息发送时不失败，
                    但是不能保证后续mq broker->consumer的链路中mq消息不丢失/消费者listener监听到mq消息后是否消费成功
                 所以这时候就需要有”对账补偿---进一步确保数据一致性“：
                    下述代码中已经有了很关键的字段”outBusinessNo“，外部业务id，活动订单创建、积分扣减、活动次数到账相关表中都有此字段，且值唯一
                        则可以借助此字段找到整条链路，然后通过一些类似user_credit_order等的流水表查询”是否已扣减积分“，从而诊断出是mq消息丢失了还是消费端失败了
                        从而可以采取重发mq消息/手动调账，达到最终一致性的目的。
             */
            log.info("积分兑换商品开始 userId:{} sku:{}", request.getUserId(), request.getSku());
            // 1. 创建兑换商品sku订单，outBusinessNo 每次创建出一个单号。
            UnpaidActivityOrderEntity unpaidActivityOrder = raffleActivityAccountQuotaService.createOrder(SkuRechargeEntity.builder()
                    .userId(request.getUserId())
                    .sku(request.getSku())
                    .outBusinessNo(RandomStringUtils.randomNumeric(12))
                    .orderTradeType(OrderTradeTypeVO.credit_pay_trade)
                    .build());
            log.info("积分兑换商品，创建订单完成 userId:{} sku:{} outBusinessNo:{}", request.getUserId(), request.getSku(), unpaidActivityOrder.getOutBusinessNo());

            // 2.支付兑换商品（积分扣减）
            String orderId = creditAdjustService.createOrder(TradeEntity.builder()
                    .userId(unpaidActivityOrder.getUserId())
                    .tradeName(TradeNameVO.CONVERT_SKU)
                    .tradeType(TradeTypeVO.REVERSE)
                    .amount(unpaidActivityOrder.getPayAmount().negate()) // 负数
                    .outBusinessNo(unpaidActivityOrder.getOutBusinessNo())
                    .build());
            log.info("积分兑换商品，支付订单完成  userId:{} sku:{} orderId:{}", request.getUserId(), request.getSku(), orderId);

            return Response.<Boolean>builder()
                    .code(ResponseCode.SUCCESS.getCode())
                    .info(ResponseCode.SUCCESS.getInfo())
                    .data(true)
                    .build();
        }catch (AppException e) {
            log.error("积分兑换商品失败 userId:{} activityId:{}",  request.getUserId(), request.getSku(), e);
            return Response.<Boolean>builder()
                    .code(e.getCode())
                    .info(e.getInfo())
                    .build();
        }  catch (Exception e) {
            log.error("积分兑换商品失败 userId:{} sku:{}", request.getUserId(), request.getSku(), e);
            return Response.<Boolean>builder()
                    .code(ResponseCode.UN_ERROR.getCode())
                    .info(ResponseCode.UN_ERROR.getInfo())
                    .data(false)
                    .build();
        }
    }
}
